package com.fubowen.lepton.ui.window

import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.animation.DecelerateInterpolator
import androidx.activity.OnBackPressedCallback
import androidx.annotation.RequiresApi
import androidx.core.animation.doOnEnd
import androidx.core.view.marginBottom
import androidx.core.view.marginTop
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.commit
import com.fubowen.lepton.R
import com.fubowen.lepton.conf.SiteConfigViewModel
import com.fubowen.lepton.databinding.FragmentWindowBinding
import com.fubowen.lepton.helper.ViewHelper.createFragmentContainerView
import com.fubowen.lepton.helper.ViewHelper.dpToPx
import com.fubowen.lepton.helper.ViewHelper.setVisible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


private const val CLICK_LIMIT = 50

class WindowFragment : Fragment() {

    private var marginTop: Int = 0
    private var marginBottom: Int = 0
    private lateinit var onBackCallback: OnBackPressedCallback
    private lateinit var binding: FragmentWindowBinding
    private val viewModel: SiteConfigViewModel by activityViewModels()
    private var downX = 0f
    private var downY = 0f
    private var currentX = 0f
    private var currentY = 0f
    private var upX = 0f
    private var upY = 0f
    private var tracker = VelocityTracker.obtain()
    private var animator: ValueAnimator? = null
    private var coverHeight = 500
    private var homeList: ArrayList<Home> = ArrayList()
    private var downTime: Long = 0

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        goBack()
        binding = FragmentWindowBinding.inflate(inflater, container, false)
        coverHeight = requireContext().dpToPx(150)
        CoroutineScope(Dispatchers.Main).launch {
            viewModel.command.observe(viewLifecycleOwner) {
                when (it.id) {
                    R.id.open_container -> {
                        shrink()
                    }
                }
            }
        }
        marginTop = binding.fragmentContainerContent.marginTop
        marginBottom = binding.fragmentContainerContent.marginBottom
        initListener()
        create().apply { expand(this, false, false) }
        return binding.root
    }

    private fun initListener() {
        binding.footerCreate.setOnClickListener {
            create().apply { expand(this, isNeedAnimator = true, isNeedInvokeChildExpand = false) }
        }
        binding.footerGoBack.setOnClickListener {
            homeList.find { it.isOpen }?.apply { expand(this) }
        }
        binding.footerClear.setOnClickListener {
            homeList.clear()
            binding.fragmentContainerContent.removeAllViews()
            binding.footerClear.isEnabled = false
        }
        binding.fragmentContainerContentShade.setOnTouchListener { _, e -> onTouchListener(e) }
    }

    private fun onTouchListener(event: MotionEvent): Boolean {
        if(viewModel.expandState.value == true){
            return false
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                animator?.cancel()
                downX = event.x
                downY = event.y
                downTime = System.currentTimeMillis()
            }

            MotionEvent.ACTION_MOVE -> {
                tracker.addMovement(event)
                currentX = event.x
                currentY = event.y
                move(event.y)
            }

            MotionEvent.ACTION_UP -> {
                upX = event.x
                upY = event.y
                tracker.computeCurrentVelocity(1000)
                executeAnimator(tracker.yVelocity, upY)
                tracker.clear()
                val offset = System.currentTimeMillis() - downTime
                if (offset < CLICK_LIMIT) {
                    findTouchView(event)
                }
            }
        }
        return true
    }

    private fun findTouchView(event: MotionEvent) {
        val count = homeList.count()
        for ((index, item) in homeList.withIndex()) {
            val point = IntArray(2)
            item.view.getLocationInWindow(point)
            val left = point[0]
            val right = left + item.view.width
            val top = point[1]
            var bottom = top + item.view.height
            if (index < count - 1) {
                val next = homeList[index + 1]
                val nextPoint = IntArray(2)
                next.view.getLocationInWindow(nextPoint)
                bottom = nextPoint[1]
            }
            if (event.rawX >= left && event.rawX <= right) {
                if (event.rawY >= top && event.rawY <= bottom) {
                    expand(item)
                    break
                }
            }
        }
    }

    /**
     * 手势滑动
     */

    private fun move(y: Float) {
        val offsetY = y - downY
        downY = y
        if (offsetY > 0) {
            moveDown(offsetY)
        } else {
            moveUp(offsetY)
        }
    }

    private fun moveDown(offsetY: Float) {
        for ((index, item) in homeList.reversed().withIndex()) {
            var translationY = item.view.translationY + offsetY
            val max = (homeList.count() - 1 - index) * coverHeight
            if (translationY > max) {
                translationY = max.toFloat()
            }
            item.view.translationY = translationY
            if (item.view.translationY != 0f) {
                item.view.setBackgroundResource(R.drawable.border_shadow_top)
            }
            if (item.view.translationY < coverHeight) {
                break
            }
        }
    }

    private fun moveUp(offsetY: Float) {
        val min = homeList.lastOrNull()?.view?.translationY
        for ((index, item) in homeList.withIndex()) {
            var translationY = item.view.translationY + offsetY
            if (translationY < 0) {
                translationY = 0f
            }
            item.view.translationY = translationY
            if (item.view.translationY == 0f && index != 0) {
                item.view.setBackgroundColor(Color.TRANSPARENT)
            }
//            if (min != null && min < coverHeight * 1.5) {
//                break
//            }
        }
    }

    private fun executeAnimator(speed: Float, initY: Float) {
        animator = ObjectAnimator.ofFloat(speed, 0f).apply {
            duration = 2000
            var current = initY
            interpolator = DecelerateInterpolator()
            addUpdateListener {
                current += it.animatedValue as Float * 0.01f
                move(current)
            }
            start()
        }
    }

    /**
     * 页面管理
     */

    private fun create(): Home {
        val container = requireContext().createFragmentContainerView()
        binding.fragmentContainerContent.addView(container)
        val shadow =
            if (homeList.isEmpty()) WindowItemFragment.TOP_BOTTOM_LEFT_RIGHT else WindowItemFragment.TOP
        val host = WindowItemFragment.newInstance(shadow, true)
        childFragmentManager.commit { add(container.id, host) }
        val home = Home(
            view = container,
            host = host
        )
        homeList.add(home)
        return home
    }

    private fun expand(
        home: Home,
        isNeedAnimator: Boolean = true,
        isNeedInvokeChildExpand: Boolean = true
    ) {
        homeList.find { it.isOpen }?.apply { isOpen = false }
        binding.fragmentContainerContent.updateLayoutParams<MarginLayoutParams> {
            topMargin = 0
            bottomMargin = 0
        }
        if (isNeedInvokeChildExpand) home.host.expand()
        home.isOpen = true
        onBackCallback.isEnabled = false
        home.view.translationZ = 10f
        binding.fragmentContainerContentShade.setVisible(false)
        fun setState() {
            val transaction = childFragmentManager.beginTransaction()
            for (item in homeList) {
                if (!item.isOpen) {
                    transaction.hide(item.host)
                    item.view.setVisible(false)
                }
            }
            transaction.commit()
        }
        if (isNeedAnimator) {
            ObjectAnimator.ofFloat(home.view, "translationY", 0f).apply {
                duration = 1000
                start()
                doOnEnd {
                    setState()
                }
            }
        } else {
            setState()
        }
        viewModel.setExpandState(true)
    }

    private fun shrink() {
        binding.fragmentContainerContent.updateLayoutParams<MarginLayoutParams> {
            topMargin = marginTop
            bottomMargin = marginBottom
        }
        homeList.find { it.isOpen }?.apply {
            val index = homeList.indexOf(this)
            val shadow =
                if (index == 0) WindowItemFragment.TOP_BOTTOM_LEFT_RIGHT else WindowItemFragment.TOP
            host.shrink(shadow)
        }
        onBackCallback.isEnabled = true
        binding.fragmentContainerContentShade.setVisible(true)
        val transaction = childFragmentManager.beginTransaction()
        for (item in homeList) {
            transaction.show(item.host)
            item.view.setVisible(true)
            item.view.translationZ = 0f
        }
        transaction.commit()
        viewModel.setExpandState(false)
    }

    private fun goBack() {
        onBackCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                homeList.find { it.isOpen }?.apply { expand(this) }
                isEnabled = false
            }
        }
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackCallback)
    }

    inner class Home(
        var host: WindowItemFragment,
        var view: View,
        var isOpen: Boolean = false
    )
}